home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 37 / Amiga Format CD37 (1999-02-16)(Future Publishing)(GB)(Track 1 of 3)[!][issue 1999-03].iso / -screenplay- / shareware / invasionforce / source / cyber3.c < prev    next >
C/C++ Source or Header  |  1999-01-09  |  58KB  |  1,710 lines

  1. /*
  2. AI Code for Invasion Force - an Explore/Conquer Strategic Wargame
  3. Copyright (C) 1996  Brannen Hough
  4.  
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */
  19. /*
  20.    cyber3.c -- artificial intelligence module for Empire II
  21.  
  22. */
  23.  
  24. /* This file contains all the routines associated with AI Type #2.
  25. */
  26.  
  27. #include "global.h"
  28.  
  29. // Local global variables - not in a .h file for others to use
  30. short Destx = -1;
  31. short Desty = -1;
  32. short Type = -1;
  33. int   FirstPath = 0; // False
  34. long  Bounds = BIG_NUMBER;
  35. int   Breakout = -1;
  36.  
  37. #define   CANT_NAVIGATE     -1
  38. #define   CITY_HERE         -2
  39. #define   UNKNOWN_COST      200
  40.  
  41. #define SHOW_AI3_MSG
  42. #ifdef SHOW_AI3_MSG
  43. #define  DEBUG_AI3(string)  if (!rtEZRequestTags(string,"Continue|Abort", \
  44.      NULL,NULL, RTEZ_Flags,EZREQF_CENTERTEXT,RT_Window,map_window, \
  45.      RT_ReqPos,REQPOS_CENTERWIN,RT_LockWindow,TRUE,TAG_END )) \
  46.      { if (AIhandle != NULL)  unpost_it(AIhandle); AIhandle = NULL; \
  47.      clean_exit(0, NULL); }
  48. #endif
  49. #ifndef SHOW_AI3_MSG
  50. #define  DEBUG_AI3(string)
  51. #endif
  52. /***************************************************************
  53. *************** Production Routines  ***************************
  54. ***************************************************************/
  55.  
  56. #define GET_REQ 10
  57. void  AI3_set_gov_prod (struct City *metro, struct GovNode *CityOwner)
  58. {
  59.     struct GovNode *Gov = (struct GovNode *)GovList.mlh_Head;
  60.     struct GovReqs  allreqs[ GET_REQ ];
  61.     int i, lowest_pri, lowest_ind, total_pri = 0;
  62.     int rand_pri;
  63.  
  64.     /* Go through the list of Governors.  We find the 10 governors with
  65.        the highest priority requests, and pick one at random based on the
  66.        relative priorities (i.e. a request of priority 5 is 5 times
  67.        more likely to be honored than a request of priority 1) and make
  68.        that requested unit. */
  69.  
  70.     /* Lets do some error checking */
  71.     CityOwner->req.req_gov = -1;
  72.     CityOwner->req.priority = 0;
  73.     CityOwner->req.type = -1;
  74.  
  75.     /* Let's initialize the 10 requests */
  76.     for (i=0; i < GET_REQ; i++) {
  77.         allreqs[i].req_gov = -1;
  78.         allreqs[i].priority = 0;
  79.         allreqs[i].type = -1;
  80.     }
  81.  
  82.     for ( ; Gov->gnode.mln_Succ; Gov = (struct GovNode *)Gov->gnode.mln_Succ) {
  83.       if (Gov->owner == player) {
  84.        LastReq.req_gov = -1;
  85.        LastReq.priority = 0;
  86.        LastReq.type = -1;
  87.             AI3_get_gov_req (Gov, metro );
  88.        if( LastReq.priority > 0 ) {
  89.       /* Add it to the list if it is one of the top whatever */
  90.       lowest_pri = 1000;
  91.       lowest_ind = -1;
  92.       for( i=0; i < GET_REQ; i++ ) {
  93.           if( allreqs[i].priority < lowest_pri ) {
  94.          lowest_ind = i;
  95.          lowest_pri = allreqs[i].priority;
  96.           }
  97.       }
  98.       if( lowest_ind != -1 ) {
  99.           allreqs[lowest_ind].req_gov = LastReq.req_gov;
  100.           allreqs[lowest_ind].priority = LastReq.priority;
  101.           allreqs[lowest_ind].type = LastReq.type;
  102.       }
  103.        }
  104.    }
  105.     } /* End for */
  106.     /* Now go through the list and total up the priorities, then pick a
  107.        random number from 1 to the total */
  108.     for( i=0; i < GET_REQ; i++) {
  109.    total_pri += allreqs[i].priority;
  110.     }
  111.     rand_pri = RangeRand( (long) (total_pri -1) ) + 1;
  112.  
  113.     /* Now select the random one we picked */
  114.     lowest_ind = -1;
  115.     while( rand_pri > 0 ) {
  116.    rand_pri -= allreqs[++lowest_ind].priority;
  117.     }
  118.     if( (lowest_ind < 0) || (lowest_ind > (GET_REQ -1)) ) {
  119.    /* Error - out of bounds */
  120.    DEBUG_AI("Out of bounds for production selection randomizer")
  121.         /* Well, make another of whatever we were making. */
  122.    CityOwner->req.req_gov = CityOwner->ID;
  123.    if( ( metro->unit_type >=0 ) && (metro->unit_type <= CARRIER) ) {
  124.        CityOwner->req.type = metro->unit_type;
  125.    }
  126.    else {
  127.        /* Need to make sure we have a decent fall-back: make an
  128.           Infantry for the owning governor - we can always use
  129.           Infantry */
  130.        metro->unit_type = RIFLE;
  131.        CityOwner->req.type = RIFLE;
  132.    }
  133.         return;
  134.     }
  135.     else {
  136.    /* Make the selected type of unit */
  137.    CityOwner->req.priority = LastReq.priority;
  138.    CityOwner->req.type = LastReq.type;
  139.    CityOwner->req.req_gov = LastReq.req_gov;
  140.    sprintf(outbuf, "Producing a %s for Governor %ld",
  141.       UnitString[CityOwner->req.type], CityOwner->req.req_gov);
  142.    DEBUG_AI(outbuf)
  143.  
  144.    /* And we set the city itself to produce this */
  145.    if (metro->unit_type != CityOwner->req.type) {  /*making a new type*/
  146.        metro->unit_type = CityOwner->req.type;
  147.        /*Tooling up penalty */
  148.        metro->unit_wip = -1 * wishbook[metro->unit_type].build/5;
  149.        sprintf (outbuf, "Tooling Up the city to build a %s",
  150.            UnitString[metro->unit_type]);
  151.        DEBUG_AI(outbuf)
  152.    }
  153.    /* unit->wip has been set to zero elsewhere.  Don't zero out
  154.       production that has already been started when we haven't
  155.       changed the type. We have enough handicaps as it is.
  156.       */
  157.     }
  158.     return;
  159. }
  160.  
  161.  
  162. void  AI3_get_gov_req( struct GovNode* Gov, struct City* metro )
  163. {
  164.     int  HaveInfAccess = 0;
  165.     int  HaveArmAccess = 0;
  166.     int  i;
  167.  
  168.     /* We assume we get the global LastReq structure with all invalid
  169.        fields, so no request is made if we don't fill them in.
  170.        */
  171.     if( IsCityTaken(Gov) ) {
  172.       /* We need ground troops if they can reach us, else Air Cav if
  173.           they are available and can reach us, else skip this city -
  174.          it's too far away and we don't do ships yet. */
  175.         if( wishbook[AIRCAV].enabled == TRUE) {
  176.           AI5_CalcPath( RIFLE, metro->col, metro->row,
  177.                 Gov->x, Gov->y, AI5_PATH_BEST );
  178.             HaveInfAccess = Path[0];
  179.             if( HaveInfAccess != -1 ) {
  180.                AI5_CalcPath( ARMOR, metro->col, metro->row,
  181.                 Gov->x, Gov->y, AI5_PATH_BEST );
  182.                 HaveArmAccess = Path[0];
  183.             }
  184.             else {
  185.                 HaveArmAccess = -1;
  186.             }
  187.           if( HaveInfAccess != -1 ) {  /* Access by ground forces */
  188.                LastReq.priority = 30;
  189.               /* No type selected yet, lets get a ground unit */
  190.                /* Make armor units if we can, if not, make infantry. */
  191.               LastReq.type = ARMOR;
  192.  
  193.              /* Now, change back if we can't make an armor or they can't
  194.                   reach the destination. */
  195.                if( (wishbook[ARMOR].enabled != TRUE) ||
  196.                 (HaveArmAccess == -1)) {
  197.                   LastReq.type = RIFLE;
  198.                }
  199.              /* And, change it back if the area of interest is
  200.                   not good for armor
  201.                   */
  202.               if((Gov->hist.TerrainCounts[HEX_PLAINS] +
  203.                Gov->hist.TerrainCounts[HEX_DESERT] +
  204.                 //Gov->hist.TerrainCounts[HEX_ARCTIC] +
  205.                Gov->hist.TerrainCounts[HEX_BRUSH])
  206.                           <
  207.                 (Gov->hist.TerrainCounts[HEX_JUNGLE] +
  208.                   Gov->hist.TerrainCounts[HEX_PEAKS] +
  209.                   Gov->hist.TerrainCounts[HEX_SWAMP] +
  210.                   Gov->hist.TerrainCounts[HEX_MOUNTAINS]) )
  211.                 LastReq.type = RIFLE;
  212.            }
  213.           else { /* No access by ground forces */
  214.                if( (AI5_GetDist( metro->col, metro->row, Gov->x,
  215.               Gov->y ) <= 12) && (wishbook[AIRCAV].enabled == TRUE) ) {
  216.                     /* distance is <= 12 AND we can make AirCav */
  217.               LastReq.type = AIRCAV;
  218.             LastReq.priority = 30;
  219.                }
  220.               /* Else, no request at all */
  221.          }
  222.         } /* End if we can make AIRCAV */
  223.         else {
  224.                 LastReq.type = ARMOR;
  225.              /* Now, change back if we can't make an armor or they can't
  226.                   reach the destination. */
  227.                if( wishbook[ARMOR].enabled != TRUE)
  228.                   LastReq.type = RIFLE;
  229.  
  230.              /* And, change it back if the area of interest is
  231.                   not good for armor
  232.                   */
  233.               if((Gov->hist.TerrainCounts[HEX_PLAINS] +
  234.                Gov->hist.TerrainCounts[HEX_DESERT] +
  235.                 //Gov->hist.TerrainCounts[HEX_ARCTIC] +
  236.                Gov->hist.TerrainCounts[HEX_BRUSH])
  237.                           <
  238.                 (Gov->hist.TerrainCounts[HEX_JUNGLE] +
  239.                   Gov->hist.TerrainCounts[HEX_PEAKS] +
  240.                   Gov->hist.TerrainCounts[HEX_SWAMP] +
  241.                   Gov->hist.TerrainCounts[HEX_MOUNTAINS]) )
  242.                 LastReq.type = RIFLE;
  243.         } /* End else can't make AIRCAV */
  244.     }
  245.     else {
  246.       switch( Gov->mode ) {
  247.        case GOV_DEFEND:
  248.            /* We're under attack!  Make some ground units to defend with */
  249.           LastReq.priority = 20;
  250.            /* But, let's only change production if it's not ARMOR */
  251.            if( metro->unit_type == ARMOR )
  252.             LastReq.type = ARMOR;
  253.            else
  254.          LastReq.type = RIFLE;
  255.            break;
  256.       case GOV_SEARCH:
  257.            /* If we have no units, make a fighter to scout with, else
  258.               make some ground troops
  259.              */
  260.            LastReq.priority = 10;
  261.           if( (Gov->hist.UnitCounts[FIGHTER] +
  262.                 Gov->hist.UnitCounts[BOMBER] < 1 ) ||
  263.                 (Gov->hist.TotalMyUnits > 10) ) {
  264.             /* if we can make fighters, make one - else make a bomber if
  265.                we can make those, else, make ground units (No Air!).
  266.                  Not going to bother with Air Cav - too slow to scout
  267.                with.
  268.              */
  269.             if( wishbook[FIGHTER].enabled == TRUE )
  270.                 LastReq.type = FIGHTER;
  271.               else if( wishbook[BOMBER].enabled == TRUE )
  272.                   LastReq.type = BOMBER;
  273.              }
  274.                /* else, make some ground pounders */
  275.                if( LastReq.type == -1 ) {
  276.              /* No type selected yet, lets get a ground unit */
  277.             /* Make armor units if we can, if not, make infantry. */
  278.              LastReq.type = ARMOR;
  279.  
  280.             /* Now, change back if we can't make an armor */
  281.              if (wishbook[ARMOR].enabled != TRUE) {
  282.                   LastReq.type = RIFLE;
  283.             }
  284.             /* And, change it back if the area of interest is
  285.                  not good for armor
  286.                 */
  287.             if((Gov->hist.TerrainCounts[HEX_PLAINS] +
  288.                  Gov->hist.TerrainCounts[HEX_DESERT] +
  289.                   //Gov->hist.TerrainCounts[HEX_ARCTIC] +
  290.                  Gov->hist.TerrainCounts[HEX_BRUSH])
  291.                             <
  292.                (Gov->hist.TerrainCounts[HEX_JUNGLE] +
  293.                  Gov->hist.TerrainCounts[HEX_PEAKS] +
  294.                   Gov->hist.TerrainCounts[HEX_SWAMP] +
  295.                  Gov->hist.TerrainCounts[HEX_MOUNTAINS]) )
  296.                  LastReq.type = RIFLE;
  297.            }
  298.           break;
  299.       } /* End switch */
  300.     }
  301.  
  302.     /* Attenuate the priority of the request for distance */
  303.     if( LastReq.priority > 0 ) {
  304.        i = AI5_GetDist (metro->col, metro->row, Gov->x, Gov->y);
  305.       LastReq.priority -= i / 2;
  306.        if (LastReq.priority < 1)  LastReq.priority = 1;
  307.     }
  308.     /* And make sure we record who made the request */
  309.     if( LastReq.type != -1 )
  310.       LastReq.req_gov = Gov->ID;
  311.  
  312.     return;
  313. }
  314.  
  315.  
  316. // This is the routine called to calculate the best
  317. //  path - it will return the direction to go in. Unless
  318. //  there is not a path - in which case it will return -1.
  319. int   AI3_calc_path( short type, short orgx, short orgy, short destx,
  320.            short desty, int ReturnFirst, int breakout )
  321. {
  322.     // This calculates the best direction to move in to get
  323.     //  from an origin to a destination based on the unit
  324.     //  type.  The map from the current player is used for
  325.     //  reference.  Unknown terrain costs more, but is still
  326.     //  considered traversable.  Time will tell if this works
  327.     //  well enough.  It is an adaptation of the A* search.
  328.     //  It is kind of slow, but not too horrific.  This is
  329.     //  just being used to calculate access for unit types
  330.     //  right now.
  331.  
  332.     struct     MapIcon *icon = (struct MapIcon *)PLAYER.icons.mlh_Head;
  333.     long        length;
  334.     long        sub_calc_move (short, short, long);
  335.  
  336.     // check that we have a valid destination
  337.     if( (destx > width) || (destx < 0) || (desty > height)
  338.         || (desty < 0) )  return (-1);  // Not on Map
  339.  
  340.     // check that we have a valid origin
  341.     if( (orgx > width) || (orgx < 0) || (orgy > height) || (orgy < 0) )
  342.         return (-1);  // Not on Map
  343.  
  344.     // check for start and end identical, just for yucks
  345.     if( (orgx == destx) && (orgy == desty) )  return (-1);
  346.  
  347.     if( MoveMap != NULL ) free (MoveMap);
  348.     // A realloc will be just as fast as clearing a memory page
  349.     //  to all zeros myself, and this makes sure there is never
  350.     //  a problem with synching up to a newly loaded map with
  351.     //  a different size.
  352.     // Try realloc instead of free and calloc later if we need
  353.     //  to get more performance.
  354.     MoveMap = (int*) calloc( width * height, sizeof (int) );
  355.  
  356.     // Check for the calloc working
  357.     if( MoveMap != NULL ) {
  358.         // First, set up the globals
  359.    FirstPath = ReturnFirst;
  360.         Destx = orgx;
  361.         Desty = orgy;
  362.         Type = type;
  363.         Bounds = BIG_NUMBER;
  364.         if( breakout > 0 ) Breakout = breakout;
  365.         else Breakout = -1;
  366.  
  367.         // Do some more initialization of the MoveMap to mark
  368.         //  the cities.
  369.         for (; icon->inode.mln_Succ; icon = (struct MapIcon *)
  370.           icon->inode.mln_Succ)  {
  371.              if( icon->type == CITY )  {
  372.                 if( icon->owner == player )
  373.                     MoveMap[icon->row * width + icon->col] = CITY_HERE;
  374.                 else MoveMap[icon->row * width + icon->col] = CANT_NAVIGATE;
  375.              }
  376.              else {
  377.                 // Icon type is NOT a city
  378.                 if( (icon->owner == player) && (opt.stacking == FALSE) )
  379.                     MoveMap[icon->row * width + icon->col] = CANT_NAVIGATE;
  380.              }
  381.         }   // End for loop
  382.  
  383.         // Now we make sure that an enemy city (or whatever) at the
  384.         //  destination hex will allow us to move - we assume the caller
  385.         //  knows what they are doing and wants to move into an enemy
  386.         //  city.
  387.         if( MoveMap[desty * width + destx] == CANT_NAVIGATE )
  388.             MoveMap[desty * width + destx] = CITY_HERE;
  389.  
  390.         // Now we call the recursive routine to help us out
  391.         // but reverse the origin and destination, so we work
  392.         // from the dest to the source, and not the reverse.
  393.         // Should make finding the path much easier.
  394.         length = sub_calc_move( destx, desty, 0);
  395.  
  396.         // Well, figure out the path and return it.  If the
  397.         //  routine returned -1, forget it.
  398.         if( length > 0 ) {
  399.             // Look at the origin - the six hexes around it.
  400.             //  the lowest value > 0 is the start of the
  401.             //  shortest path.
  402.             int  i, k = 0;
  403.             int  least = BIG_NUMBER;
  404.             int  dir = -1;
  405.             short newx, newy;
  406.  
  407.             if (destx - orgx < 0)  k = 3;
  408.             if (destx == orgx)     k = 6;
  409.             if (desty - orgy < 0)  k += 1;
  410.             if (desty == orgy)     k += 2;
  411.             for (i=0; i < 6; i++) {
  412.                 if( AI1_calc_dir(DirArray[k][i], orgx, orgy,
  413.                   &newx, &newy) > 0 ) {
  414.                     if( (MoveMap[newy * width + newx] > 0) &&
  415.                         (MoveMap[newy * width + newx] < least) ) {
  416.                             least = MoveMap[newy * width + newx];
  417.                             dir = DirArray[k][i];
  418.                     } // End if
  419.                 } // End if we have a hex in that dir
  420.             } // End for
  421.             // If we have a best direction, return it
  422.             if (dir != -1)  return dir;
  423.         }
  424.     }
  425.  
  426.     // And if all else fails, return fails.
  427.     return (-1);
  428. }
  429.  
  430.  
  431. long   sub_calc_move( short orgx, short orgy, long CumCost )
  432. {
  433.     // Here we recurse - expanding each one of the six hexes
  434.     //  around the starting hex - but only if it is a zero
  435.     //  in the MoveMap.  Thus we cover each hex once only.
  436.  
  437.     int     i;
  438.     int     j = -1;
  439.     int     index = orgy * width + orgx;    // Done once for
  440.                                             // efficiency
  441.     int     cost;
  442.     long    lowest = BIG_NUMBER;
  443.  
  444.     // First, did we make it?  If so set the bounds if this is the
  445.     //  shortest path so far.
  446.     if( (orgx == Destx) && (orgy == Desty) )  {
  447.         if( CumCost < Bounds ) Bounds = CumCost;
  448.         return CumCost;
  449.     }
  450.  
  451.     // Check if we have already been here and determined we
  452.     //  can't move here
  453.     if( MoveMap[index] == CANT_NAVIGATE )  return (-1);
  454.  
  455.     if( Breakout == 0 ) return -1;
  456.     if( Breakout != -1 ) {
  457.         if(--Breakout == 0) return (-1);
  458.     }
  459.     // Next we check for the hex being navagable by the unit
  460.     //  type.  Note that an unknown hex is not unnavagable
  461.     if( get(PLAYER.map, orgx, orgy) == HEX_UNEXPLORED )  {
  462.         cost = UNKNOWN_COST;
  463.     }
  464.     else {
  465.         // A friendly city here is navagable, regardless of
  466.         //  the underlying terrain.
  467.         if( MoveMap[index] == CITY_HERE ) {
  468.                 cost = 10;
  469.         }
  470.         else {
  471.                 // Get cost based on terrain
  472.                 cost = movement_cost_table[Type][get(PLAYER.map, orgx, orgy)];
  473.        }
  474.     }
  475.  
  476.     // If we can't move here mark the space
  477.     if( cost == CANT_NAVIGATE ) {
  478.         MoveMap[index] = cost;
  479.         return (-1);
  480.     }
  481.  
  482.     // Now that we know we CAN move here, we can add this space to the
  483.     //  running total
  484.     CumCost += cost;
  485.  
  486.     // Check the Bounds - no use expanding from here if we already have
  487.     //  a longer path than the shortest path found so far.
  488.     if( CumCost >= Bounds )  {
  489.    // Mark the space as unnavagable - no use checking here again
  490.         //MoveMap[index] = CANT_NAVIGATE;
  491.    return (-1);
  492.     }
  493.  
  494.     // Check the MoveMap space.  If it is zero OR positive and greater
  495.     //  than the new total (so that the new total is a shorter path to
  496.     //  get there) then record the new total in that space
  497.     if( (MoveMap[index] == 0) || (MoveMap[index] > CumCost)
  498.         || (MoveMap[index] == CITY_HERE) ) {
  499.         // Need extra variables for new location
  500.         short   newx, newy;
  501.         int     k = 0;
  502.  
  503.         // Replace the value here
  504.         MoveMap[index] = CumCost;
  505.  
  506.         // Now expand, calling this routine recursively
  507.         if (Destx - orgx < 0)  k = 3;
  508.         if (Destx == orgx)     k = 6;
  509.         if (Desty - orgy < 0)  k += 1;
  510.         if (Desty == orgy)     k += 2;
  511.        for (i=0; i < 6; i++) {
  512.          //check each direction from here
  513.          if ( AI1_calc_dir
  514.            (DirArray[k][i], orgx, orgy, &newx, &newy) > 0) {
  515.               j = sub_calc_move( newx, newy, CumCost);
  516.                 // Record the shortest path of the six
  517.               if( (j > 0) && ( FirstPath ) ) return( j );
  518.                         if( (j > 0) && (j < lowest) ) lowest = j;
  519.                 } // End if
  520.        } // End for loop
  521.  
  522.         // If we have a good path, return it
  523.         if( lowest < BIG_NUMBER )  return lowest;
  524.    } // End if we want to expand from this hex
  525.  
  526.     // Else, return no path
  527.     return (-1);
  528. }
  529.  
  530.  
  531. struct GovNode*  AI3_locate_gov( struct City* metro )
  532. {
  533.     struct GovNode *Gov = (struct GovNode *)GovList.mlh_Head;
  534.     struct GovNode *NewGov = NULL;
  535.  
  536.     for ( ; Gov->gnode.mln_Succ; Gov = (struct GovNode *)Gov->gnode.mln_Succ) {
  537.    if ((Gov->owner == player) &&
  538.        ((Gov->type == GOV_CITY) || (Gov->type == GOV_PORT) ||
  539.       (Gov->type == GOV_ISLAND))
  540.        && (Gov->x == metro->col) && (Gov->y == metro->row)) {
  541.        /* We found the right city governor. */
  542.        return( Gov );
  543.    }  /* End if */
  544.     } /* End for loop */
  545.  
  546.     DEBUG_AI("Couldn't find the governor, making one")
  547.     /* Never found it */
  548.     NewGov = AI3_add_gov( metro );
  549.     /* And, let's set up the governors area of interest */
  550.     AI3_setup_area_of_interest (NewGov, 5, 9);
  551.     return (NewGov);
  552. }
  553.  
  554.  
  555. struct GovNode*  AI3_add_gov( struct City* metro )
  556. {
  557.     struct GovNode *new_gov = AllocVec((int)sizeof(*new_gov),MEMF_CLEAR);
  558.     int i;
  559.     short terrain;
  560.     short targx, targy;
  561.     struct MapIcon* icon;
  562.  
  563.     new_gov->x = metro->col;
  564.     new_gov->y = metro->row;
  565.     new_gov->searchx = -1;
  566.     new_gov->searchy = -1;
  567.     new_gov->targx = -1;
  568.     new_gov->targy = -1;
  569.     new_gov->mode = GOV_SEARCH;
  570.     new_gov->flags = 0;
  571.     new_gov->owner = player;
  572.     if (port_cityP(metro)) {
  573.    new_gov->type = GOV_PORT;
  574.    /* Next, we need to check if we are an island city */
  575.    /* Check all six hexes around us - if there is no land or
  576.       city in any of the six, then we are an island */
  577.    for( i=0; i<6; i++ ) {
  578.        /* Check each direction for a city or land hex */
  579.        if (AI1_calc_dir (i, metro->col, metro->row, &targx,
  580.           &targy) != -1) {
  581.       /* Check for a land hex, or a city icon there */
  582.       terrain = get(PLAYER.map, targx, targy);
  583.       if( (terrain >= 1) && (terrain <= 11) ) {
  584.           /* its a land */
  585.           goto EXIT_ISLAND_SEARCH;
  586.       }
  587.       icon = (struct MapIcon *)PLAYER.icons.mlh_Head;
  588.       for (; icon->inode.mln_Succ; icon = (struct MapIcon *)
  589.           icon->inode.mln_Succ)
  590.           if ((icon->type == CITY) &&
  591.          (icon->col == targx) &&
  592.          (icon->row == targy))  goto EXIT_ISLAND_SEARCH;
  593.        }
  594.    }
  595.    new_gov->type = GOV_ISLAND;
  596.       EXIT_ISLAND_SEARCH: new_gov->ID = NewGov;
  597.     }
  598.     else {
  599.        new_gov->type = GOV_CITY;
  600.        new_gov->ID = NewGov;
  601.     }
  602.     NewGov += RangeRand(3L); // Just to mix up the numbers a little
  603.  
  604.     sprintf (outbuf, "Creating Governor %ld at %ld,%ld",
  605.         new_gov->ID, new_gov->x, new_gov->y);
  606.     DEBUG_AI(outbuf)
  607.  
  608.  
  609.     /* Add the new governor to the list of governors */
  610.     AddTail((struct List *)&GovList,(struct Node *)new_gov);
  611.     return( new_gov );
  612. }
  613.  
  614.  
  615. void  AI3_setup_area_of_interest( struct GovNode* Gov, int inner, int outer )
  616. {
  617.     /* Set up the immediate area of interest */
  618.     Gov->startx = Gov->x - inner;
  619.     Gov->starty = Gov->y - inner;
  620.     Gov->endx = Gov->x + inner;
  621.     Gov->endy = Gov->y + inner;
  622.     if (!wrap) {
  623.         if (Gov->startx < 0) Gov->startx = 0;
  624.         if (Gov->starty < 0) Gov->starty = 0;
  625.         if (Gov->endx > width - 1) Gov->endx = width - 1;
  626.         if (Gov->endy > height - 1) Gov->endy = height - 1;
  627.     }
  628.     else {
  629.    if (Gov->startx < 0) Gov->startx += width;
  630.    if (Gov->starty < 0) Gov->starty += height;
  631.    if (Gov->endx > width - 1) Gov->endx -= width;
  632.    if (Gov->endy > height - 1) Gov->endy -= height;
  633.     }
  634.     /* Set up the Extended area of interest */
  635.     Gov->Estartx = Gov->x - outer;
  636.     Gov->Estarty = Gov->y - outer;
  637.     Gov->Eendx = Gov->x + outer;
  638.     Gov->Eendy = Gov->y + outer;
  639.     if (!wrap) {
  640.         if (Gov->Estartx < 0) Gov->Estartx = 0;
  641.         if (Gov->Estarty < 0) Gov->Estarty = 0;
  642.         if (Gov->Eendx > width - 1) Gov->Eendx = width - 1;
  643.         if (Gov->Eendy > height - 1) Gov->Eendy = height - 1;
  644.     }
  645.     else {
  646.    if (Gov->Estartx < 0) Gov->Estartx += width;
  647.    if (Gov->Estarty < 0) Gov->Estarty += height;
  648.    if (Gov->Eendx > width - 1) Gov->Eendx -= width;
  649.    if (Gov->Eendy > height - 1) Gov->Eendy -= height;
  650.     }
  651.  
  652.     /* sprintf (outbuf,
  653.        "For Gov %ld, Startx:%ld, Starty:%ld, Endx:%ld, Endy:%ld",
  654.        Gov->ID, Gov->startx, Gov->starty, Gov->endx, Gov->endy);
  655.        DEBUG_AI(outbuf)
  656.        */
  657.     return;
  658. }
  659.  
  660.  
  661. void  AI3_play_turn( int new_units )
  662. {
  663.     int   MaxLooping = 5000;
  664.     /* Here we may want to look around, give out some orders to units,
  665.        and execute orders to units in a loop until we have done all the
  666.        moves possible for the units.
  667.        */
  668.     AI3_do_all_histograms();
  669.     AI3_give_orders();
  670.     /* We'll add a little failsafe so we don't spend eternity here */
  671.     while( (MaxLooping > 0) && (!DoUnitActions(40,60)) )  MaxLooping--;
  672.     if( MaxLooping <= 0 )
  673.    DEBUG_AI3("Error: Exitting AI player's turn - out of actions!")
  674.     return;
  675. }
  676.  
  677.  
  678. void  AI3_do_all_histograms()
  679. {
  680.     struct GovNode *Gov = (struct GovNode *)GovList.mlh_Head;
  681.     /* First we update the picture for all the Governors */
  682.     for ( ; Gov->gnode.mln_Succ; Gov = (struct GovNode *)Gov->gnode.mln_Succ)
  683.    if (Gov->owner == player) {
  684.        AI1_do_one_histogram (Gov);
  685.         AI3_set_gov_mode (Gov);
  686.    }
  687.     /* End for loop */
  688. }
  689.  
  690.  
  691. void  AI3_set_gov_mode( struct GovNode* Gov )
  692. {
  693.     enum GovMode new_mode = GOV_SEARCH;
  694.  
  695.     // Now, if we have enemy forces in the area, set to defend
  696.     if(( Gov->hist.TotalEUnits > 0 ) || (Gov->hist.EnemyCounts[CITY] > 0) )
  697.          new_mode = GOV_DEFEND;
  698.  
  699.     if( new_mode != Gov->mode ) {
  700.         Gov->mode = new_mode;
  701.         // clear all orders
  702.         AI1_clear_all_orders( Gov );
  703.     }
  704.     // And if we lost the city, set the taken flag - this will also
  705.     // clear orders if the taken flag gets set.
  706.     AI1_set_gov_mode( Gov );
  707.  
  708.     return;
  709. }
  710.  
  711.  
  712.  
  713. void  AI3_give_orders( )
  714. {
  715.     struct  Unit   *unit = (struct Unit *)unit_list.mlh_Head;
  716.  
  717.     for (;unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
  718.         if ((unit->owner == player) && (unit->move > 0) &&
  719.             (unit->orders == NULL)) {
  720.             /* No orders yet, let's set some
  721.           Ok, we own the unit, and it has moves left, and has a Governor
  722.           owner, and has no standing orders
  723.           Don't just stand there, do something!
  724.           */
  725.             AI3_orders_for_unit( unit );
  726.        } /* end if owner and has moves left */
  727.     /* End For */
  728. }
  729.  
  730. void  AI3_orders_for_unit( struct Unit* unit )
  731. {
  732.     struct GovNode *Gov = NULL;
  733.  
  734.     Gov = AI1_FindOwner (unit);
  735.     if (Gov != NULL) {
  736.          if( IsCityTaken( Gov ) ) {
  737.           AI3_taken_orders( unit, Gov );
  738.         } /* End if CityTaken */
  739.         else {
  740.           switch( Gov->mode ) {
  741.                case GOV_DEFEND:  {
  742.                AI3_defend_orders( unit, Gov );
  743.                break;
  744.                } /* End if mode = defend */
  745.                case GOV_SEARCH: {
  746.                AI3_search_orders( unit, Gov );
  747.                break;
  748.                }
  749.              default: {
  750.                AI3_default_orders( unit, Gov );
  751.                break;
  752.              }
  753.           } /* End switch */
  754.         } /* End else city is not taken */
  755.     } /* End if Gov is not equal to NULL */
  756.     else {
  757.       /* Can't find owner */
  758.       AI3_default_orders( unit, Gov );
  759.     }
  760. }
  761.  
  762. void  AI3_taken_orders( struct Unit* unit, struct GovNode* Gov )
  763. {
  764.     char outbuf[20];
  765.     struct GovNode* Gov2 = NULL;
  766.     struct MapIcon* icon = NULL;
  767.     /* For taken cities, we want the air units to retreat to another
  768.        city (if possible) or else attack any enemy in sight.  Aircav
  769.        should try to retake the city if they can.  Armor and Infantry
  770.        should also retake the city like AI #2.
  771.        */
  772.     switch( unit->type ) {
  773.     case FIGHTER:
  774.    /* Deliberate fall through */
  775.     case BOMBER:
  776.    Gov2 = AI3_FindClosestCityGov( unit, unit->fuel );
  777.    if( Gov2 ) {
  778.        ComputerGiveOrders
  779.       (unit, C_ORDER_GOTO, Gov2->x, Gov2->y, -1, -1, -1);
  780.        /* And defect to that Governor */
  781.        sprintf (outbuf, "%ld / %ld", Gov->ID, NewUnit++);
  782.        name_unit( unit, outbuf);
  783.    }
  784.    else {
  785.        /* No one else in range - try to take someone with us */
  786.        ComputerGiveOrders
  787.       (unit, C_ORDER_HUNT, -1, -1, -1, -1, 6);
  788.    }
  789.    break;
  790.     case AIRCAV:
  791.    if( AI5_GetDist( unit->col, unit->row, Gov->x, Gov->y )
  792.        <= unit->fuel ) {
  793.        /* We are in range of our own Governor, so go retake it.
  794.           Do or die! */
  795.        ComputerGiveOrders
  796.       (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  797.    }
  798.    else {
  799.        /* Too far to reach it */
  800.        /* Find nearest city to hole up in */
  801.        Gov2 = AI3_FindClosestCityGov( unit, unit->fuel );
  802.        if( Gov2 ) {
  803.       ComputerGiveOrders
  804.           (unit, C_ORDER_GOTO, Gov2->x, Gov2->y, -1, -1, -1);
  805.       /* And defect to that Governor */
  806.       sprintf (outbuf, "%ld / %ld", Gov->ID, NewUnit++);
  807.       name_unit( unit, outbuf);
  808.        }
  809.        else {
  810.       /* What now ? I must be out here for some reason... */
  811.       icon = AI3_FindClosestEnemyCity( unit, unit->fuel );
  812.       if( icon ) {
  813.           /* Go get it! */
  814.           ComputerGiveOrders
  815.          (unit, C_ORDER_GOTO, icon->col, icon->row, -1, -1, -1);
  816.       }
  817.        }
  818.    }
  819.     case RIFLE:
  820.    /* Deliberate fall through */
  821.     case ARMOR:
  822.    /* Come and retake the city */
  823.    ComputerGiveOrders
  824.        (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  825.    break;
  826.     default:
  827.    DEBUG_AI("Unknown unit type - ignoring")
  828.    break;
  829.     }
  830. }
  831.  
  832. void  AI3_defend_orders( struct Unit* unit, struct GovNode* Gov )
  833. {
  834.     struct MapIcon* icon = NULL;
  835.     /* On defense, we have the fighters and bombers seek out enemy units
  836.        and attack them - within their fuel constraints.  Units with less
  837.        than half their fuel will return to their city for fueling before
  838.        assaulting enemy units - unless, of course, they run into them on
  839.        the way.  Aircav will still try to go for the nearest enemy city
  840.        to try and take it (it may be why we are in defend mode).  Ground
  841.        units will try to attack enemy units or enemy cities (if they are
  842.        closer). This may need some more thought!
  843.           How about this - we make the defining factor whether there are
  844.        enemy units about.  For fighter and bomber aircraft, if there were
  845.        no enemy units, then go back to recon (they certainly can't help
  846.        take enemy cities).
  847.        */
  848.     switch( unit->type ) {
  849.     case FIGHTER:
  850.    /* Deliberate fall through */
  851.     case BOMBER:
  852.    if( Gov->hist.TotalEUnits > 0 ) {
  853.        if( unit->fuel > (wishbook[unit->type].range / 2) ) {
  854.       /* Go hunting */
  855.       ComputerGiveOrders
  856.           (unit, C_ORDER_HUNT, -1, -1, -1, -1, 6);
  857.        }
  858.        else {
  859.       /* Return for more fuel */
  860.       ComputerGiveOrders
  861.           (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  862.        }
  863.    }
  864.    else {
  865.        /* Must be another city we saw - go back to recon */
  866.        if( (unit->col == Gov->x) && (unit->row == Gov->y) )
  867.       ComputerGiveOrders
  868.           (unit, C_ORDER_RECON, -1, -1, Gov->x, Gov->y, -1 );
  869.        else
  870.       ComputerGiveOrders
  871.           (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  872.    }
  873.    break;
  874.     case AIRCAV:
  875.    icon = AI3_FindClosestEnemyCity( unit, unit->fuel );
  876.    if( icon ) {
  877.        /* Enemy city in range. Go get it! */
  878.        ComputerGiveOrders
  879.       (unit, C_ORDER_GOTO, icon->col, icon->row, -1, -1, -1);
  880.    }
  881.    else {
  882.        /* None in range - must be enemy units about */
  883.        /* This may not be smart - these are expensive units. */
  884.        if( Gov->hist.TotalEUnits > 0 ) {
  885.       ComputerGiveOrders
  886.           (unit, C_ORDER_HUNT, -1, -1, -1, -1, 6);
  887.        }
  888.        else {
  889.       /* No one around to attack.  Put in storage */
  890.       if(( unit->col == Gov->x) && ( unit->row == Gov->y) ) {
  891.           ComputerGiveOrders
  892.          (unit, C_ORDER_SENTRY, -1, -1, -1, -1, -1);
  893.       }
  894.       else {
  895.           /* Return for more fuel */
  896.           ComputerGiveOrders
  897.          (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  898.       }
  899.        }
  900.    }
  901.    break;
  902.     case RIFLE:
  903.    /* Deliberate fall through */
  904.     case ARMOR:
  905.    if( Gov->hist.TotalEUnits > 0 ) {
  906.        if( (unit->col > Gov->Estartx) &&
  907.           (unit->col < Gov->Eendx ) &&
  908.           (unit->row > Gov->Estarty) &&
  909.          (unit->row < Gov->Eendy) ) {
  910.          DEBUG_AI("Put 'em on the hunt")
  911.           ComputerGiveOrders
  912.               (unit, C_ORDER_HUNT, -1, -1, -1, -1, 6);
  913.        } /* End of if unit in governors extended zone */
  914.        else {
  915.          /* Maybe there is a city nearby to attack? */
  916.          icon = AI3_FindClosestEnemyCity
  917.              ( unit, 10 );
  918.          if( icon ) {
  919.              ComputerGiveOrders
  920.                 (unit, C_ORDER_GOTO, icon->col, icon->row,
  921.               -1, -1, -1);
  922.          }
  923.          else {
  924.               ComputerGiveOrders
  925.                  (unit, C_ORDER_GOTO, Gov->x, Gov->y,
  926.              -1, -1, -1);
  927.          }
  928.        } /* End else not in governors extended range */
  929.    }
  930.    else {
  931.        /* No enemy units - must be a city to attack */
  932.        icon = AI3_FindClosestEnemyCity( unit, 100 );
  933.        if( icon ) {
  934.          ComputerGiveOrders
  935.               (unit, C_ORDER_GOTO, icon->col, icon->row, -1,
  936.           -1, -1);
  937.        }
  938.        else {
  939.          /* Go back to random wandering - nothing better to do */
  940.             ComputerGiveOrders
  941.             (unit, C_ORDER_RANDOM, -1, -1, -1, -1, -1);
  942.        }
  943.    }
  944.    break;
  945.     default:
  946.       DEBUG_AI("Unknown unit type - ignoring")
  947.        break;
  948.     } /* End switch */
  949. }
  950.  
  951. void  AI3_search_orders( struct Unit* unit, struct GovNode* Gov )
  952. {
  953.     struct GovNode* Gov2 = NULL;
  954.     struct MapIcon* icon = NULL;
  955.     /* Here we want the aircraft to search for new cities to take,
  956.        the ground units just wander around randomly like before.
  957.        */
  958.     switch( unit->type ) {
  959.     case FIGHTER:
  960.    /* Deliberate fall through */
  961.     case BOMBER:
  962.    /* Let's go exploring */
  963.    if( (unit->col == Gov->x) && (unit->row == Gov->y) ) {
  964.        ComputerGiveOrders
  965.       (unit, C_ORDER_RECON, -1, -1, Gov->x, Gov->y, -1 );
  966.    }
  967.    else {
  968.        /* return for fuel */
  969.        ComputerGiveOrders
  970.       (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  971.    }
  972.    break;
  973.     case AIRCAV:
  974.    /* Except for AirCav - put them in storage until needed */
  975.    if( city_hereP( unit->col, unit->row ) != NULL ) {
  976.        /* We are in a city, so go to sentry */
  977.        ComputerGiveOrders
  978.       (unit, C_ORDER_SENTRY, -1, -1, -1, -1, -1);
  979.    }
  980.    else {
  981.        /* Try to get to a city */
  982.        if( AI5_GetDist (unit->col, unit->row, Gov->x, Gov->y)
  983.       > unit->fuel ) {
  984.       /* Need to get to the nearest city-gov and get put in
  985.          storage there */
  986.       Gov2 = AI3_FindClosestCityGov( unit, unit->fuel );
  987.       if( Gov2 ) {
  988.           ComputerGiveOrders
  989.          (unit, C_ORDER_GOTO, Gov2->x, Gov2->y,
  990.           -1, -1, -1);
  991.       }
  992.       else {
  993.           /* Now what ?  Try to take an enemy city of course ..*/
  994.           icon = AI3_FindClosestEnemyCity( unit, unit->fuel );
  995.           if( icon ) {
  996.          /* Go get it! */
  997.          ComputerGiveOrders
  998.              (unit, C_ORDER_GOTO, icon->col, icon->row,
  999.               -1, -1, -1);
  1000.           }
  1001.           else {
  1002.          /* How can this be?  Why are we out here? We never
  1003.             leave the nest unless attacking a city. */
  1004.          DEBUG_AI3("AirCav movement problem - can't reach any city")
  1005.           }
  1006.       }
  1007.        }
  1008.        else {
  1009.       /* go home */
  1010.       ComputerGiveOrders
  1011.           (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  1012.        }
  1013.    }
  1014.    break;
  1015.     case RIFLE:
  1016.    /* Deliberate fall through */
  1017.     case ARMOR:
  1018.    /* Let's do some self preservation - check if we are hurt */
  1019.    if( unit->damage > 0 ) {
  1020.        /* We're damaged - head for home to get repaired */
  1021.        ComputerGiveOrders
  1022.       ( unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1 );
  1023.    }
  1024.    else {
  1025.        /* Wander around like a drunken sailer! */
  1026.        ComputerGiveOrders
  1027.       (unit, C_ORDER_RANDOM, -1, -1, -1, -1, -1);
  1028.    }
  1029.    break;
  1030.     default:
  1031.    DEBUG_AI("Unknown unit type - ignoring")
  1032.    break;
  1033.     }
  1034. }
  1035.  
  1036. void  AI3_default_orders( struct Unit* unit, struct GovNode* Gov )
  1037. {
  1038.     struct GovNode* Gov2;
  1039.     char outbuf[20];
  1040.     /* Should never be called - ever.  This is a failsafe in case the mode
  1041.        for the Governor gets set to something really strange.  So, anyway,
  1042.        in order to keep everything moving smoothly we will have default
  1043.        orders set up for each type of unit - have aircraft go to the nearest
  1044.        city and go on sentry there, and have the ground units wander about
  1045.        randomly.
  1046.        */
  1047.     if( Gov ) {
  1048.    DEBUG_AI3("Error!  Calling default orders - problem in Governor mode?")
  1049.     }
  1050.     else {
  1051.    DEBUG_AI3("Error!  Calling default orders - unit has no Governor!")
  1052.     }
  1053.     switch( unit->type ) {
  1054.     case FIGHTER:
  1055.    /* Deliberate fall through */
  1056.     case BOMBER:
  1057.    /* Deliberate fall through */
  1058.     case AIRCAV:
  1059.    /* Find the closest city and go there, or if in a city already
  1060.       then just go to sentry mode */
  1061.    if( city_hereP( unit->col, unit->row ) != NULL )
  1062.        ComputerGiveOrders
  1063.       (unit, C_ORDER_SENTRY, -1, -1, -1,-1, -1);
  1064.    else {
  1065.        /* Find owner city and go there */
  1066.        Gov2 = AI1_FindOwner (unit);
  1067.        if( Gov2 ) {
  1068.       /* The failsafe is set to the unit's fuel - either make
  1069.          it there or die trying! */
  1070.       ComputerGiveOrders
  1071.           (unit, C_ORDER_GOTO, Gov2->x, Gov2->y, -1, -1, -1);
  1072.        }
  1073.        else {
  1074.       /* Can't find owner - go to closest city Governor and set
  1075.          that as the unit's owner */
  1076.       Gov2 = AI3_FindClosestCityGov( unit, unit->fuel );
  1077.       if( Gov2 ) {
  1078.           ComputerGiveOrders
  1079.          (unit, C_ORDER_GOTO, Gov2->x, Gov2->y,
  1080.           -1, -1, -1);
  1081.           /* And make this Governor the owner of the unit */
  1082.           sprintf (outbuf, "%ld / %ld", Gov2->ID, NewUnit++);
  1083.           name_unit( unit, outbuf);
  1084.       }
  1085.       else {
  1086.           /* else all is lost - have it try to pound on enemies
  1087.              until it drops */
  1088.           ComputerGiveOrders
  1089.          (unit, C_ORDER_HUNT, -1, -1, -1, -1, 20);
  1090.       }
  1091.        }
  1092.    }
  1093.    break;
  1094.     case RIFLE:
  1095.    /* Deliberate fall through */
  1096.     case ARMOR:
  1097.    ComputerGiveOrders (unit, C_ORDER_RANDOM, -1, -1, -1,-1, -1);
  1098.    /* Make sure we have an owning Governor */
  1099.    Gov = AI1_FindOwner (unit);
  1100.    if( Gov == NULL ) {
  1101.        /* No owner - set it to the closest city Governor */
  1102.        Gov = AI3_FindClosestCityGov( unit, 100 );
  1103.        sprintf (outbuf, "%ld / %ld", Gov->ID, NewUnit++);
  1104.        name_unit( unit, outbuf);
  1105.    }
  1106.    break;
  1107.     default:
  1108.    DEBUG_AI("Unknown unit type - ignoring")
  1109.    break;
  1110.     }
  1111. }
  1112.  
  1113.  
  1114. struct MapIcon* AI3_FindClosestEnemyCity( struct Unit* unit, int limit )
  1115. {
  1116.     /* Modifying this routine to yield the closest enemy city that the
  1117.        unit can actually REACH - it's no good telling an infantry unit
  1118.        that the closest enemy city is across the bay...
  1119.        */
  1120.     int  closest = BIG_NUMBER;
  1121.     int  i;
  1122.     struct MapIcon *closestEnemy = NULL;
  1123.     struct MapIcon *icon = (struct MapIcon *) PLAYER.icons.mlh_Head;
  1124.  
  1125.     for (; icon->inode.mln_Succ; icon = (struct MapIcon *)
  1126.       icon->inode.mln_Succ) {
  1127.         if( (icon->owner != player) && (icon->type == CITY) ) {
  1128.            i = AI5_GetDist ( unit->col, unit->row, icon->col,
  1129.                 icon->row );
  1130.             AI5_CalcPath( unit->type, unit->col, unit->row, icon->col,
  1131.                 icon->row, AI5_PATH_BEST );
  1132.            if( (i < limit) && (i < closest) &&
  1133.               (Path[0] != -1 ) ) {
  1134.                   closest = i;
  1135.                   closestEnemy = icon;
  1136.            }
  1137.        }
  1138.     }
  1139.     return( closestEnemy );
  1140. }
  1141.  
  1142.  
  1143. struct GovNode* AI3_FindClosestCityGov( struct Unit* unit, int limit )
  1144. {
  1145.     int  closest = BIG_NUMBER;
  1146.     int  i;
  1147.     struct GovNode *closestCity = NULL;
  1148.     struct GovNode *gov = (struct GovNode*) GovList.mlh_Head;
  1149.  
  1150.     for (; gov->gnode.mln_Succ; gov = (struct GovNode*)gov->gnode.mln_Succ) {
  1151.         if( (gov->owner == player) &&
  1152.        ((gov->type == GOV_CITY) || (gov->type == GOV_PORT) ||
  1153.       (gov->type == GOV_ISLAND) )) {
  1154.        i = AI5_GetDist( unit->col, unit->row, gov->x, gov->y );
  1155.        if( (i < limit) && (i < closest) ) {
  1156.       closest = i;
  1157.       closestCity = gov;
  1158.        }
  1159.    }
  1160.     }
  1161.     return( closestCity );
  1162. }
  1163.  
  1164.  
  1165.  
  1166. int  AI3_do_unit_actions()
  1167. {
  1168.     struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  1169.     struct GovNode *Gov = NULL;
  1170.     int         Done = TRUE;
  1171.     static struct Unit* lastunit = NULL;
  1172.     static int    times_accessed = 0;
  1173.  
  1174.     for ( ; unit->unode.mln_Succ; unit = (struct Unit *) unit->unode.mln_Succ)
  1175.         if ((unit->owner==player) && (unit->move > 0)) {
  1176.        /* Added this last to only give any one unit 10 chances to
  1177.           move - if it hasn't by then, skip over it and go to the
  1178.           next.  This has the pleasant effect that the unit that
  1179.           couldn't move in 10 tries will get another 10 tries to
  1180.           move, and so on, until it finally gets skipped for this
  1181.           turn. */
  1182.        if ( (lastunit != unit) || (times_accessed < 10) )  {
  1183.           if( lastunit != unit ) {
  1184.               lastunit = unit;
  1185.               times_accessed = 0;
  1186.           }
  1187.           else {
  1188.               times_accessed++;
  1189.           }
  1190.           /* Skip Sentry units */
  1191.           if ((unit->orders != NULL) &&
  1192.               (unit->orders->reserved == C_ORDER_SENTRY))
  1193.               continue;
  1194.          /* Take a look around us and react to enemy units + cities */
  1195.          if( AI3_look_around ( unit ) )   return (FALSE);
  1196.           /* if we have orders, execute them */
  1197.          if (unit->orders != NULL) {
  1198.              AI3_execute_standing_order(unit);
  1199.               return (FALSE);
  1200.          }
  1201.  
  1202.          else {
  1203.                 /* We don't have orders, so try to wing it! */
  1204.               Gov = AI1_FindOwner (unit);
  1205.               if (Gov != NULL) {
  1206.                  DEBUG_AI("No orders, no enemy around. Huh?")
  1207.                 /* Just skip this unit this turn - the
  1208.                     governor willgive new orders next turn. */
  1209.                 unit->move = 0;
  1210.              } /* End if Gov != NULL */
  1211.              else {
  1212.                 DEBUG_AI("Cannot find unit owner to ad lib turn")
  1213.                  DEBUG_AI("For Now, Forget IT!")
  1214.                  sprintf (outbuf, "%s Unit %s at %ld,%ld",
  1215.                    UnitString[unit->type], unit->name,
  1216.                    unit->col, unit->row);
  1217.                  DEBUG_AI(outbuf)
  1218.                 unit->move = 0;
  1219.               } /* End else Gov == NULL */
  1220.          } /* End else figure something out */
  1221.        } /* End if the unit is not the same one we accessed 10 times */
  1222.        else {
  1223.          /* Last unit IS this unit and times accessed >= 10. */
  1224.           /* This should ensure that we never get stuck forever - we
  1225.              will always finish - removes a potential infinite loop
  1226.             when there are TWO enemy planes, over water, that can be
  1227.             attacked by ground troops.  They will ping pong between
  1228.              them forever. */
  1229.          unit->move-=10;
  1230.        }
  1231.     } /* End if we own unit and it has moves left */
  1232.     /* End for loop */
  1233.     times_accessed = 0;
  1234.     lastunit = NULL;
  1235.     return (Done);
  1236. }
  1237.  
  1238.  
  1239.  
  1240. void AI3_computer_give_orders(struct Unit *unit,int suborder,short destx,
  1241.     short desty,short orgx,short orgy,int etc)
  1242. {
  1243.     struct GovNode* Gov = NULL;
  1244.     struct Order *order=AllocVec((long)sizeof(*order),MEMF_CLEAR);
  1245.  
  1246.    clear_orders(unit);
  1247.    if (order) {
  1248.       order->destx = destx;
  1249.       order->desty = desty;
  1250.       unit->orders = order;
  1251.       order->type = ORDER_NONE;
  1252.       if (orgx == -1)  order->orgx = unit->col;
  1253.       else  order->orgx = orgx;
  1254.       if (orgy == -1)  order->orgy = unit->row;
  1255.       else  order->orgy = orgy;
  1256.       order->processed = FALSE;
  1257.       order->etc = etc;
  1258.       order->reserved = suborder;       /* using reserved for computer
  1259.                                   orders so I don't tread on the human
  1260.                                   player's tokens. */
  1261.  
  1262.       /* This is set up to be in the order they are used most often,
  1263.          and set up so that more order types can be added as needed.
  1264.        */
  1265.       switch (suborder) {
  1266.       case C_ORDER_HEADTO:
  1267.        /* Standard stuff only */
  1268.         sprintf (outbuf, "%s %s C_ORDER_HEADTO from %ld,%ld to %ld,%ld",
  1269.         UnitString[unit->type], unit->name, order->orgx,
  1270.         order->orgy, order->destx, order->desty);
  1271.         DEBUG_AI(outbuf)
  1272.  
  1273.        break;
  1274.       case C_ORDER_RECON:
  1275.        /* We need to figure out where to recon to - based on the Gov's
  1276.           current status of recon elements. */
  1277.        Gov = AI1_FindOwner( unit );
  1278.        if( Gov ) {
  1279.          AI3_select_recon_hex( unit, Gov );
  1280.          /* And let's set a failsafe of 1/2 our fuel -1 */
  1281.          /* And if this is not an aircraft for some reason
  1282.        (a screwup) this will just get set to -1 anyway */
  1283.          order->etc = wishbook[unit->type].range / 2 - 1;
  1284.        }
  1285.        else {
  1286.          /* Can't find the owner - will get sorted out next turn */
  1287.        }
  1288.        break;
  1289.       case C_ORDER_GOTO:
  1290.        /* Standard stuff only */
  1291.             break;
  1292.       case C_ORDER_RANDOM:
  1293.        /* Standard stuff only */
  1294.        break;
  1295.       case C_ORDER_HUNT:
  1296.        /* Standard stuff only */
  1297.        break;
  1298.       case C_ORDER_SENTRY:
  1299.        /* Standard stuff only */
  1300.        break;
  1301.       }
  1302.    }
  1303. }
  1304.  
  1305.  
  1306. void AI3_select_recon_hex( struct Unit* unit, struct GovNode* Gov )
  1307. {
  1308.     /* The recon command is special - each Governor will search out
  1309.        it's edges in a particular pattern.  There are two variables
  1310.        in the GovNode struct - searchx and searchy - that record
  1311.        where the governor is searching to currently.  Thus, the next
  1312.        available recon resource can be given the next destination
  1313.        in turn, and they recyle when all have been exhausted, so that
  1314.        the explorers turn into patrollers automatically.
  1315.        */
  1316.     BOOL    UseNext = FALSE;
  1317.     int      Reach;
  1318.     int      i;
  1319.     short   targx,  targy;
  1320.  
  1321.     if ((Gov->x == unit->col) && (Gov->y == unit->row)) {
  1322.    /* We have an aircraft, fueled, at home city
  1323.       Let's use them to keep on exploring
  1324.       */
  1325.    Reach = wishbook[unit->type].range / 2 - 1;
  1326.    if( Reach <= 0 ) {
  1327.        /* Something awry here - not a plane */
  1328.        clear_orders(unit);
  1329.        return;
  1330.    }
  1331.    if( Gov->searchx == -1) {
  1332.        /* No searches done yet, start with the first */
  1333.        UseNext = TRUE;
  1334.    }
  1335.    for (i = 0; i < 21; i++) {
  1336.        switch (i) {
  1337.       /* These are the short range ones */
  1338.        case 20:    /* Wrap around to zero again! */
  1339.       /* Deliberate fall through */
  1340.        case 0:
  1341.       targx = Gov->endx;
  1342.       targy = Gov->starty;
  1343.       break;
  1344.        case 1:
  1345.       targx = Gov->startx;
  1346.       targy = Gov->endy;
  1347.       break;
  1348.        case 2:
  1349.       targx = Gov->endx;
  1350.       targy = Gov->endy;
  1351.       break;
  1352.        case 3:
  1353.       targx = Gov->startx;
  1354.       targy = Gov->starty;
  1355.       break;
  1356.       /* And these are the long ranged ones. */
  1357.       /* Mixed them up to cover more ground faster */
  1358.        case 15:     /* North */
  1359.          targx = Gov->x;
  1360.          targy = Gov->y - Reach;
  1361.           break;
  1362.        case 4:     /* NorthNorthEast */
  1363.          targx = Gov->x + 5;
  1364.          targy = Gov->y - Reach + 2;
  1365.           break;
  1366.        case 8:     /* EastNorthEast */
  1367.          targx = Gov->x + Reach - 2;
  1368.          targy = Gov->y - 4;
  1369.           break;
  1370.        case 12:     /* East */
  1371.          targx = Gov->x + Reach;
  1372.          targy = Gov->y;
  1373.           break;
  1374.        case 5:     /* EastSouthEast */
  1375.          targx = Gov->x + Reach - 2;
  1376.          targy = Gov->y + 4;
  1377.           break;
  1378.        case 9:     /* SouthSouthEast */
  1379.          targx = Gov->x + 5;
  1380.          targy = Gov->y + Reach - 2;
  1381.           break;
  1382.        case 13:     /* South */
  1383.          targx = Gov->x;
  1384.          targy = Gov->y + Reach;
  1385.           break;
  1386.        case 6:     /* SouthSouthWest */
  1387.          targx = Gov->x - 5;
  1388.          targy = Gov->y + Reach - 2;
  1389.           break;
  1390.        case 10:     /* WestSouthWest */
  1391.          targx = Gov->x - Reach + 2;
  1392.          targy = Gov->y + 4;
  1393.           break;
  1394.        case 14:    /* West */
  1395.          targx = Gov->x - Reach;
  1396.          targy = Gov->y;
  1397.           break;
  1398.        case 7:     /* WestNorthWest */
  1399.          targx = Gov->x - Reach + 2;
  1400.          targy = Gov->y - 4;
  1401.           break;
  1402.        case 11:     /* NorthNorthWest */
  1403.          targx = Gov->x - 5;
  1404.          targy = Gov->y - Reach + 2;
  1405.           break;
  1406.        case 16:    /* NorthPlus */
  1407.          targx = Gov->x - 2;
  1408.          targy = Gov->y - Reach;
  1409.           break;
  1410.        case 18:    /* NorthMinus */
  1411.          targx = Gov->x + 2;
  1412.          targy = Gov->y - Reach;
  1413.           break;
  1414.        case 17:    /* SouthPlus */
  1415.          targx = Gov->x - 2;
  1416.          targy = Gov->y + Reach;
  1417.           break;
  1418.        case 19:    /* SouthMinus */
  1419.          targx = Gov->x + 2;
  1420.          targy = Gov->y + Reach;
  1421.           break;
  1422.        default:
  1423.       DEBUG_AI("Unknown case in select_search_pattern")
  1424.                 break;
  1425.        } /* End switch (i) */
  1426.        /* Let's make sure the target point is on the map */
  1427.        if (targx < 0) {
  1428.          if (!wrap) targx = 0;
  1429.          else targx = targx + (width - 1);
  1430.        }
  1431.        if (targx > (width -1)) {
  1432.           if (!wrap) targx = width - 1;
  1433.          else targx = targx - (width - 1);
  1434.        }
  1435.        if (targy < 0) {
  1436.          if (!wrap) targy = 0;
  1437.           else targy = targy + (height - 1);
  1438.        }
  1439.        if (targy > (height - 1)) {
  1440.          if (!wrap) targy = height - 1;
  1441.          else targy = targy - (height - 1);
  1442.        }
  1443.        /* If we have been told to UseNext, then do it */
  1444.        if( UseNext == TRUE ) {
  1445.          unit->orders->destx = targx;
  1446.          unit->orders->desty = targy;
  1447.             Gov->searchx = targx;
  1448.             Gov->searchy = targy;
  1449.             return;
  1450.        }
  1451.        /* Now, check for the current search target */
  1452.        if( ( Gov->searchx == targx ) && (Gov->searchy == targy ) ) {
  1453.       /* we found the last one sent out - use the next one */
  1454.           UseNext = TRUE;
  1455.        }
  1456.    } /* end for loop */
  1457.    /* If we get here, there is a problem */
  1458.    clear_orders(unit);
  1459.    return;
  1460.     } /* End if at home  */
  1461.     else {
  1462.    /* Deliver selves to home city */
  1463.    DEBUG_AI("Search or Conquer, unit delivering to owner")
  1464.             ComputerGiveOrders (unit, C_ORDER_GOTO, Gov->x,
  1465.               Gov->y, -1, -1, -1);
  1466.     } /* End else */
  1467. }
  1468.  
  1469.  
  1470. int  AI3_look_around( struct Unit* unit)
  1471. {
  1472.     int          i, k;
  1473.     short        targx, targy;
  1474.     struct City* metro;
  1475.     struct GovNode *FoundGov = NULL;
  1476.  
  1477.     for (i = 0; i < 6; i++) {
  1478.    if (AI1_calc_dir (i, unit->col, unit->row, &targx, &targy) != -1) {
  1479.        if ((metro = city_hereP (targx, targy)) &&
  1480.       (metro->owner != player)) {
  1481.       /* if there is a city here and we don't own it, this
  1482.          call will create a Governor for it if there isn't
  1483.          one already.
  1484.          */
  1485.       //FoundGov = AI3_locate_gov( metro );
  1486.       /* And let the new Gov take a look around */
  1487.       //AI1_do_one_histogram (FoundGov);
  1488.       //AI3_set_gov_mode (FoundGov);
  1489.       /* Then, try to take it */
  1490.         if ((unit->type == RIFLE) || (unit->type == ARMOR)
  1491.           || (unit->type == AIRCAV)) {
  1492.           /* Go for it! */
  1493.           /* DEBUG_AI("Jumping enemy city") */
  1494.           /* We can check here that we can GET to the city
  1495.              and at least tried to take it, bogged down, etc.
  1496.              */
  1497.           if( move_unit_xy (unit, targx, targy) <= -2) {
  1498.              return (1);
  1499.           }
  1500.           /* Else, look around for something else to attack -
  1501.            another city, an enemy unit, etc.
  1502.            */
  1503.         } /* End if can take city */
  1504.       if( (unit->type == BOMBER) && (metro->owner != 0)) {
  1505.           if( (!unit->orders) ||
  1506.          (unit->orders->reserved != C_ORDER_RECON)) {
  1507.          /* if we can't take it - bomb it! But it is no use
  1508.             bombing a neutral city. Also, if we are on recon,
  1509.             don't bomb either.
  1510.             */
  1511.          move_unit_xy (unit, targx, targy);
  1512.          return (1);
  1513.           }
  1514.       }
  1515.        } /* End if city here and it ain't ours! */
  1516.    } /* End if hex here */
  1517.     } /* End For loop */
  1518.     for (i = 0; i < 6; i++) {
  1519.    if (AI1_calc_dir (i, unit->col, unit->row, &targx, &targy) != -1) {
  1520.        if (((k = hex_owner(targx,targy)) > 0)&&(k!=player)) {
  1521.       if((!unit->orders) ||
  1522.          (unit->orders->reserved != C_ORDER_RECON) ) {
  1523.           /*DEBUG_AI("Jumping enemy unit") */
  1524.           /* Add the if statement so that we keep trying to attack
  1525.               an enemy until we succeed or have tried each direction
  1526.               once.  There are cases (like an aircraft over water
  1527.               attacked by a ground unit) where attacks can't take
  1528.               place. */
  1529.           if( move_unit_xy (unit, targx, targy) <= -2 ) {
  1530.               /* We successfully attacked, ran out of gas, couldn't
  1531.                   move, etc. */
  1532.               return (1);
  1533.           }
  1534.             if( !AI3_AssertUnit(unit) ) return (1);
  1535.       }
  1536.       /* Else, try a different enemy unit */
  1537.        }  /* End if bad guy here */
  1538.    } /* End if hex here */
  1539.     } /* End for loop */
  1540.     return (0);
  1541. }
  1542.  
  1543. void  AI3_execute_standing_order( struct Unit* unit )
  1544. {
  1545.     int     result;
  1546.  
  1547.     if ((unit->orders == NULL) || (unit->orders->type != ORDER_NONE)) {
  1548.    DEBUG_AI("Big problem in execute_standing_order - no orders!")
  1549.    sprintf (outbuf, "%s %s has no standing orders to execute - aborting",
  1550.        UnitString[unit->type], unit->name);
  1551.    DEBUG_AI(outbuf)
  1552.    return;
  1553.     }
  1554.  
  1555.     switch (unit->orders->reserved) {
  1556.         case  C_ORDER_HEADTO:
  1557.             result = AI1_command_headto(unit);
  1558.             if( (result <= 0) && (AI3_AssertUnit(unit)) )
  1559.                 clear_orders(unit);   /* problem or done */
  1560.             break;
  1561.         case  C_ORDER_RANDOM:
  1562.             AI3_command_random(unit);
  1563.             break;
  1564.         case  C_ORDER_HUNT:
  1565.           result = AI2_command_hunt(unit);
  1566.            if( (result < 0) && (AI3_AssertUnit(unit)) )
  1567.                 clear_orders(unit);  /* wouldn't work */
  1568.            break;
  1569.         case  C_ORDER_RECON:
  1570.             result = AI3_command_recon(unit);
  1571.             if( (result <= 0) && (AI3_AssertUnit(unit)) )
  1572.                  clear_orders(unit);  /* problem */
  1573.             break;
  1574.         default:
  1575.             DEBUG_AI("Unknown command type found in execute_standing_order!")
  1576.             break;
  1577.     }
  1578.     return;
  1579. }
  1580.  
  1581.  
  1582. void  AI3_command_random( struct Unit* unit )
  1583. {
  1584.     if( unit->orders->destx >= 0 ) {
  1585.         /* destx has the last direction we were headed */
  1586.         if( move_unit_dir(unit,(enum Direction) unit->orders->destx) != -1 )
  1587.             return;
  1588.     }
  1589.     /* Pick a new random direction to head in */
  1590.     if( unit->orders )
  1591.         unit->orders->destx = RangeRand(6L);
  1592. }
  1593.  
  1594.  
  1595.  
  1596.  
  1597. int   AI3_command_recon( struct Unit* unit )
  1598. {
  1599.     int  result;
  1600.  
  1601.     /* With this order we go out to the destination, then, once we
  1602.        reach it or hit 1/2 fuel, replace the destination with the
  1603.        origin (home) and head for it.
  1604.        */
  1605.     /* Very first thing is to check that if we are on the last half
  1606.        of our fuel, we are headed home.  If not, start heading home.
  1607.        */
  1608.     if( (wishbook[unit->type].range > 0) &&
  1609.        (unit->fuel <= wishbook[unit->type].range / 2) &&
  1610.        ((unit->orders->destx != unit->orders->orgx) ||
  1611.        (unit->orders->desty != unit->orders->orgy)) ) {
  1612.            unit->orders->destx = unit->orders->orgx;
  1613.            unit->orders->desty = unit->orders->orgy;
  1614.           unit->orders->etc = unit->fuel;
  1615.     }
  1616.     /* Next, we'll use the AI1_command_headto routine to do the grunt
  1617.        work for us (since this command is really two of those commands
  1618.        back to back).
  1619.        */
  1620.     result = AI1_command_headto( unit );
  1621.     if( result < 0 ) {
  1622.       /* problem - check where we are, and head home if not there */
  1623.        /* -1 blocked, just can't get there */
  1624.        /* -2 watchdog ran out */
  1625.        if( result == -1 ) return result;  /* report the problem */
  1626.        /* else watchdog. Head for home */
  1627.  
  1628.        if( (AI3_AssertUnit(unit)) && (unit->orders) &&
  1629.             ((unit->orders->destx != unit->orders->orgx) ||
  1630.            (unit->orders->desty != unit->orders->orgy)) ) {
  1631.               unit->orders->destx = unit->orders->orgx;
  1632.                unit->orders->desty = unit->orders->orgy;
  1633.                unit->orders->etc = unit->fuel;
  1634.                return 1;
  1635.         }
  1636.     }
  1637.     if( result == 0 ) {
  1638.        /* Done - check for which leg it was on */
  1639.        if( (AI3_AssertUnit(unit)) && (unit->orders) &&
  1640.             ((unit->orders->destx != unit->orders->orgx) ||
  1641.            (unit->orders->desty != unit->orders->orgy)) ) {
  1642.               /* Now head for home */
  1643.                unit->orders->destx = unit->orders->orgx;
  1644.                unit->orders->desty = unit->orders->orgy;
  1645.                unit->orders->etc = unit->fuel;
  1646.               return 1;  /* Don't clear orders yet */
  1647.        }
  1648.     }
  1649.     return result;
  1650. }
  1651.  
  1652.  
  1653. int  AI3_AssertUnit( struct Unit* CheckUnit )
  1654. {
  1655.     /* Let's take a page from C++ now and create an
  1656.        assertion function to test that a unit still
  1657.        exists before we operate on it after a move
  1658.        */
  1659.     struct Unit *unit = (struct Unit *)unit_list.mlh_Head;
  1660.     for( ; unit->unode.mln_Succ; unit = (struct Unit *) unit->unode.mln_Succ)
  1661.         if( CheckUnit == unit )  return TRUE;
  1662.     //End for loop
  1663.     (void)rtEZRequestTags ( "Error: Unit assertion failed", "Dang!",NULL,NULL,
  1664.          RT_DEFAULT,TAG_END);
  1665.     return FALSE;
  1666. }
  1667.  
  1668.  
  1669. void  AI3_Add_Lib( struct Unit* unit )
  1670. {
  1671.     // This routine sets orders for units that have no orders in the middle
  1672.     //   of a turn.  Without this aircraft will be 'skipped' for a turn,
  1673.     //   which usually runs them out of fuel and crashes them.
  1674.  
  1675.     struct GovNode* Gov = AI1_FindOwner( unit );
  1676.     // For ground units, the default is OK.  For air units we need to send
  1677.     //   them back to a landing spot.
  1678.     switch( unit->type ) {
  1679.       case FIGHTER:
  1680.       case BOMBER:
  1681.       case AIRCAV: {
  1682.    if( Gov ) {
  1683.      // Go home
  1684.      ComputerGiveOrders
  1685.       (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  1686.    }
  1687.    else {
  1688.      Gov = AI3_FindClosestCityGov( unit, unit->fuel );
  1689.      if( Gov ) {
  1690.        // Land at the closest
  1691.      ComputerGiveOrders
  1692.       (unit, C_ORDER_GOTO, Gov->x, Gov->y, -1, -1, -1);
  1693.      }
  1694.      else {
  1695.        // Forget it, we're toast
  1696.        unit->fuel -= unit->move / 60;
  1697.        unit->move = 0;
  1698.        if( unit->fuel <= 0 ) Remove ((struct Node*)unit);
  1699.        return;
  1700.      }
  1701.    }
  1702.    break;
  1703.       }
  1704.       default: {
  1705.    unit->move = 0;
  1706.    break;
  1707.       }
  1708.     }
  1709. }
  1710.